package com.jintian.smart.kernel.flowable.engine;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.auth.IOrgUnitQuery;
import com.jintian.smart.kernel.core.dto.OrgUnit;
import com.jintian.smart.kernel.flowable.domain.entity.ActGrant;
import com.jintian.smart.kernel.flowable.domain.entity.BizFlow;
import com.jintian.smart.kernel.flowable.domain.entity.FlowAct;
import com.jintian.smart.kernel.flowable.domain.model.ActivityImpl;
import com.jintian.smart.kernel.flowable.domain.model.FlowImpl;
import com.jintian.smart.kernel.flowable.mapper.IActGrantMapper;
import com.jintian.smart.kernel.flowable.mapper.IBizFlowMapper;
import com.jintian.smart.kernel.flowable.mapper.IFlowActMapper;
import com.jintian.smart.kernel.module.Model;
import org.apache.commons.io.IOUtils;
import org.beetl.sql.core.query.LambdaQuery;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Component
public class FlowLoader implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private IBizFlowMapper bizFlowMapper;
    @Autowired
    private IFlowActMapper flowActMapper;
    @Autowired
    private IActGrantMapper actGrantMapper;
    @Autowired
    private IOrgUnitQuery operatorQuery;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public FlowImpl load(String modelName, String flowCode, Integer flowVersion) throws Exception {
        if (flowVersion == null) {
            flowVersion = 0;
        }
        ClassLoader cl = null;
        if (modelName != null && this.applicationContext.getParent() != null) {
            Model model = this.applicationContext.getBean("model$" + modelName, Model.class);
            cl = model.getClassLoader();
        } else {
            cl = this.applicationContext.getClassLoader();
        }
        FlowImpl flow;
        InputStream in = null;
        try {
            in = cl.getResourceAsStream("flow/" + flowCode + "." + flowVersion + ".json");
            if (in == null) {
                in = cl.getResourceAsStream("flow/" + flowCode + ".json");
            }
            if (in == null) {
                flow = new FlowImpl();
            } else {
                flow = this.objectMapper.readValue(IOUtils.toString(in, "utf-8"), FlowImpl.class);
            }
        } finally {
            IOUtils.closeQuietly(in);
        }
        LambdaQuery<BizFlow> queryFlow = bizFlowMapper.createLambdaQuery().andEq(BizFlow::getFlowCode, flowCode);
        // 倒排序，使用高版本
        List<BizFlow> list = queryFlow.and(queryFlow.condition().orEq(BizFlow::getFlowVersion, flowVersion).orEq(BizFlow::getFlowVersion, 0))
                .order(BizFlow::getFlowVersion, false).select();
        if (list.size() > 0) {
            this.fillBizFlowToFlowModel(list.get(0), flow);
        }

        LambdaQuery<FlowAct> queryAct = flowActMapper.createLambdaQuery().andEq(FlowAct::getFlowCode, flowCode);
        // 倒排序，使用高版本
        List<FlowAct> actList = queryAct
                .and(queryAct.condition().orEq(FlowAct::getFlowVersion, flowVersion).orEq(FlowAct::getFlowVersion, 0))
                .order(FlowAct::getFlowVersion, false).select();
        Set<String> done = new HashSet<>();
        for (FlowAct act : actList) {
            if (done.contains(act.getActCode())) {
                continue;
            }
            done.add(act.getActCode());
            ActivityImpl activity = flow.findActivity(act.getActName());
            if (activity == null) {
                activity = new ActivityImpl();
                activity.setName(act.getActCode());
                flow.getActvities().add(activity);
            }
            if (act.getBizRules() != null)
                activity.setBizRules(act.getBizRules());
            if (act.getExecuteRules() != null)
                activity.setExecuteRules(act.getExecuteRules());
            if (act.getFlowRules() != null)
                activity.setFlowRules(act.getFlowRules());
            if (act.getTransformRules() != null)
                activity.setTransformRules(act.getTransformRules());
            if (act.getTaskAttributes() != null)
                activity.setTaskAttributes(act.getTaskAttributes());
            activity.setFormUrl(new StringUtil().firstNonEmpty(act.getFormUrl(), flow.getFormUrl()));
        }
        return flow;
    }

    private void fillBizFlowToFlowModel(BizFlow flow, FlowImpl flowModel) {
        if (StringUtil.isNotEmpty(flow.getFormUrl())) {
            flowModel.setFormUrl(flow.getFormUrl());
        }
        if (flow.getSilence() != null) {
            flowModel.setSilence(flow.getSilence());
        }
        if (flow.getStartRules() != null) {
            flowModel.setStartRules(flow.getStartRules());
        }
        if (flow.getBizRules() != null) {
            flowModel.setBizRules(flow.getBizRules());
        }
        if (flow.getBizTables() != null) {
            flowModel.setBizTables(flow.getBizTables());
        }
        if (flow.getContextVariables() != null) {
            flowModel.setContextVariables(flow.getContextVariables());
        }
        if (StringUtil.isNotBlank(flow.getBizEntity())) {
            flowModel.setBizEntity(flow.getBizEntity());
        }

    }

    public List<OrgUnit> getGrantOrgUnit(String flowCode, Integer flowVersion, String actCode) {
        List<ActGrant> list = this.actGrantMapper.findGrantOrgByFlowCode(flowCode, actCode, flowVersion);
        if (list.size() == 0) {
            list = this.actGrantMapper.findGrantOrgByFlowCode(flowCode, actCode, null);
        }
        if (list.size() == 0) {
            list = this.actGrantMapper.findGrantOrgByFlowCode(flowCode, "*", flowVersion);
        }
        if (list.size() == 0) {
            list = this.actGrantMapper.findGrantOrgByFlowCode(flowCode, "*", null);
        }
        List<OrgUnit> ret = new ArrayList<>();
        for (ActGrant grant : list) {
            OrgUnit unit = operatorQuery.query(grant.getOrgKind().toLowerCase(), grant.getOrgCode(), false);
            if (unit != null) {
                ret.add(unit);
            }
        }
        return ret;
    }
}
